﻿// *********************************************************************
// Copyright (c) Microsoft Corporation.  All rights reserved.
// Licensed under the MIT License
// *********************************************************************
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Fabric;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ServiceFabric.Services.Runtime;

namespace DataX.SimulatedData.DataGenService
{
    [EventSource(Name = "DataX.SimulatedData-DataX.SimulatedData.DataGenService")]
internal sealed class ServiceEventSource : EventSource
{
    public static readonly ServiceEventSource Current = new ServiceEventSource();

    // Instance constructor is private to enforce singleton semantics
    private ServiceEventSource() : base() { }

    #region Keywords
    // Event keywords can be used to categorize events. 
    // Each keyword is a bit flag. A single event can be associated with multiple keywords (via EventAttribute.Keywords property).
    // Keywords must be defined as a public class named 'Keywords' inside EventSource that uses them.
    public static class Keywords
    {
        public const EventKeywords Requests = (EventKeywords)0x1L;
        public const EventKeywords ServiceInitialization = (EventKeywords)0x2L;
    }
    #endregion

    #region Events
    // Define an instance method for each event you want to record and apply an [Event] attribute to it.
    // The method name is the name of the event.
    // Pass any parameters you want to record with the event (only primitive integer types, DateTime, Guid & string are allowed).
    // Each event method implementation should check whether the event source is enabled, and if it is, call WriteEvent() method to raise the event.
    // The number and types of arguments passed to every event method must exactly match what is passed to WriteEvent().
    // Put [NonEvent] attribute on all methods that do not define an event.
    // For more information see https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource.aspx

    [NonEvent]
    public void Message(string message, params object[] args)
    {
        if (this.IsEnabled())
        {
            string finalMessage = string.Format(message, args);
            Message(finalMessage);
        }
    }

    private const int MessageEventId = 1;
    [Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")]
    public void Message(string message)
    {
        if (this.IsEnabled())
        {
            WriteEvent(MessageEventId, message);
        }
    }

    [NonEvent]
    public void ServiceMessage(StatelessServiceContext serviceContext, string message, params object[] args)
    {
        if (this.IsEnabled())
        {
            string finalMessage = string.Format(message, args);
            ServiceMessage(
                serviceContext.ServiceName.ToString(),
                serviceContext.ServiceTypeName,
                serviceContext.InstanceId,
                serviceContext.PartitionId,
                serviceContext.CodePackageActivationContext.ApplicationName,
                serviceContext.CodePackageActivationContext.ApplicationTypeName,
                serviceContext.NodeContext.NodeName,
                finalMessage);
        }
    }

    // For very high-frequency events it might be advantageous to raise events using WriteEventCore API.
    // This results in more efficient parameter handling, but requires explicit allocation of EventData structure and unsafe code.
    // To enable this code path, define UNSAFE conditional compilation symbol and turn on unsafe code support in project properties.
    private const int ServiceMessageEventId = 2;
    [Event(ServiceMessageEventId, Level = EventLevel.Informational, Message = "{7}")]
    private
#if UNSAFE
        unsafe
#endif
        void ServiceMessage(
        string serviceName,
        string serviceTypeName,
        long replicaOrInstanceId,
        Guid partitionId,
        string applicationName,
        string applicationTypeName,
        string nodeName,
        string message)
    {
#if !UNSAFE
        WriteEvent(ServiceMessageEventId, serviceName, serviceTypeName, replicaOrInstanceId, partitionId, applicationName, applicationTypeName, nodeName, message);
#else
            const int numArgs = 8;
            fixed (char* pServiceName = serviceName, pServiceTypeName = serviceTypeName, pApplicationName = applicationName, pApplicationTypeName = applicationTypeName, pNodeName = nodeName, pMessage = message)
            {
                EventData* eventData = stackalloc EventData[numArgs];
                eventData[0] = new EventData { DataPointer = (IntPtr) pServiceName, Size = SizeInBytes(serviceName) };
                eventData[1] = new EventData { DataPointer = (IntPtr) pServiceTypeName, Size = SizeInBytes(serviceTypeName) };
                eventData[2] = new EventData { DataPointer = (IntPtr) (&replicaOrInstanceId), Size = sizeof(long) };
                eventData[3] = new EventData { DataPointer = (IntPtr) (&partitionId), Size = sizeof(Guid) };
                eventData[4] = new EventData { DataPointer = (IntPtr) pApplicationName, Size = SizeInBytes(applicationName) };
                eventData[5] = new EventData { DataPointer = (IntPtr) pApplicationTypeName, Size = SizeInBytes(applicationTypeName) };
                eventData[6] = new EventData { DataPointer = (IntPtr) pNodeName, Size = SizeInBytes(nodeName) };
                eventData[7] = new EventData { DataPointer = (IntPtr) pMessage, Size = SizeInBytes(message) };

                WriteEventCore(ServiceMessageEventId, numArgs, eventData);
            }
#endif
    }

    private const int ServiceTypeRegisteredEventId = 3;
    [Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}", Keywords = Keywords.ServiceInitialization)]
    public void ServiceTypeRegistered(int hostProcessId, string serviceType)
    {
        WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType);
    }

    private const int ServiceHostInitializationFailedEventId = 4;
    [Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed", Keywords = Keywords.ServiceInitialization)]
    public void ServiceHostInitializationFailed(string exception)
    {
        WriteEvent(ServiceHostInitializationFailedEventId, exception);
    }

    // A pair of events sharing the same name prefix with a "Start"/"Stop" suffix implicitly marks boundaries of an event tracing activity.
    // These activities can be automatically picked up by debugging and profiling tools, which can compute their execution time, child activities,
    // and other statistics.
    private const int ServiceRequestStartEventId = 5;
    [Event(ServiceRequestStartEventId, Level = EventLevel.Informational, Message = "Service request '{0}' started", Keywords = Keywords.Requests)]
    public void ServiceRequestStart(string requestTypeName)
    {
        WriteEvent(ServiceRequestStartEventId, requestTypeName);
    }

    private const int ServiceRequestStopEventId = 6;
    [Event(ServiceRequestStopEventId, Level = EventLevel.Informational, Message = "Service request '{0}' finished", Keywords = Keywords.Requests)]
    public void ServiceRequestStop(string requestTypeName, string exception = "")
    {
        WriteEvent(ServiceRequestStopEventId, requestTypeName, exception);
    }
    #endregion

    #region Private methods
#if UNSAFE
        private int SizeInBytes(string s)
        {
            if (s == null)
            {
                return 0;
            }
            else
            {
                return (s.Length + 1) * sizeof(char);
            }
        }
#endif
    #endregion
}
}
